#pragma once
#include "dsp/DSP.h"
#include "dsp/IIR.h"
#include "dsp/RingBuffer.h"
#include "dsp/IIRUtil.h"

namespace yzrilyzr_dsp{
	ECLASS(FDNReverb2, public DSP){
		static constexpr int const kDelayLengths[8]{37, 87, 181, 271, 359, 593, 688, 721};

	public:
	u_sample lowpassCoeff=0;
	u_sample highpassCoeff=0;
	u_normal_11 feedbackGain=0;
	u_normal_01 wetRatio=0;
	~FDNReverb2(){}
	FDNReverb2();
	FDNReverb2(u_sample lowpassCoeff, u_sample highpassCoeff, u_normal_11 feedbackGain, u_normal_01 wetRatio) :
		feedbackGain(feedbackGain),
		lowpassCoeff(lowpassCoeff),
		highpassCoeff(highpassCoeff),
		wetRatio(wetRatio){
		for(u_index i=0; i < 8; i++){
			mDecayFilters[i]=nullptr;
		}
	}
	u_sample procDsp(u_sample input) override{
		u_sample output=0;
		for(u_index row=0; row < 8; row++){
			mTempInputs[row]=mInputWeights[row] * input;
			for(u_index col=0; col < 8; col++){
				mTempInputs[row]+=mFeedbackMatrix[row][col] * mOutputHistory[col];
			}
		}
		for(u_index i=0; i < 8; i++){
			mDelayLines[i].write(mTempInputs[i]);
			mOutputHistory[i]=mDecayFilters[i]->procDsp(mDelayLines[i].getFromNewest(kDelayLengths[i]));
			// Prevent denormals (very small numbers that can cause performance issues)
			mOutputHistory[i]+=1e-18f;
			mOutputHistory[i]-=1e-18f;
			output+=mOutputWeights[i] * mOutputHistory[i] * 0.5f;
		}
		return wetRatio * output + (1.0f - wetRatio) * input;
	}
	void init(u_sample_rate sampleRate) override{
		u_normal_11 gains[]{feedbackGain, 1 + feedbackGain, feedbackGain, feedbackGain,
			feedbackGain, feedbackGain, feedbackGain, feedbackGain};
		for(u_index i=0; i < 8; i++){
			mOutputHistory[i]=0;
			mInputWeights[i]=1;
			mOutputWeights[i]=(i % 2 == 0)?1.0f / 8.0f:-1.0f / 8.0f;
		}
		for(u_index row=0; row < 8; row++){
			for(u_index col=0; col < 8; col++){
				mFeedbackMatrix[row][col]=gains[(8 + (col - row)) % 8];
			}
		}
		for(u_index i=0; i < 8; i++){
			mDelayLines[i].ensureCapacity((u_sample)kDelayLengths[i]);
			mDecayFilters[i]=IIRUtil::newC1C3IIRFilter((u_sample)sampleRate / kDelayLengths[i],
													   lowpassCoeff, highpassCoeff);
		}
	}
	void resetMemory() override{
		for(u_index i=0; i < 8; i++){
			mDelayLines[i].fill(0);
			mDecayFilters[i]->resetMemory();
			mOutputHistory[i]=0;
			mTempInputs[i]=0;
		}
	}
	std::shared_ptr<DSP> cloneDSP() override{
		return std::make_shared < FDNReverb2>(lowpassCoeff, highpassCoeff, feedbackGain, wetRatio);
	}
	void cloneParam(DSP * obj1)override{}
	yzrilyzr_lang::String toString()const override;
	private:
	std::shared_ptr<IIR> mDecayFilters[8];
	RingBufferSample mDelayLines[8];
	u_sample mFeedbackMatrix[8][8];
	u_sample mOutputHistory[8];
	u_sample mInputWeights[8];
	u_sample mOutputWeights[8];
	u_sample mTempInputs[8];
	};
}
